// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/sender/video_sender.h"

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <vector>

#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/test/simple_test_tick_clock.h"
#include "media/base/fake_single_thread_task_runner.h"
#include "media/base/video_frame.h"
#include "media/cast/cast_environment.h"
#include "media/cast/constants.h"
#include "media/cast/logging/simple_event_subscriber.h"
#include "media/cast/net/cast_transport_config.h"
#include "media/cast/net/cast_transport_impl.h"
#include "media/cast/net/pacing/paced_sender.h"
#include "media/cast/sender/fake_video_encode_accelerator_factory.h"
#include "media/cast/sender/video_frame_factory.h"
#include "media/cast/test/utility/default_config.h"
#include "media/cast/test/utility/video_utility.h"
#include "media/video/fake_video_encode_accelerator.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media {
namespace cast {

    namespace {
        static const uint8_t kPixelValue = 123;
        static const int kWidth = 320;
        static const int kHeight = 240;

        using testing::_;
        using testing::AtLeast;

        void SaveOperationalStatus(OperationalStatus* out_status,
            OperationalStatus in_status)
        {
            DVLOG(1) << "OperationalStatus transitioning from " << *out_status << " to "
                     << in_status;
            *out_status = in_status;
        }

        class TestPacketSender : public PacketTransport {
        public:
            TestPacketSender()
                : number_of_rtp_packets_(0)
                , number_of_rtcp_packets_(0)
                , paused_(false)
            {
            }

            // A singular packet implies a RTCP packet.
            bool SendPacket(PacketRef packet, const base::Closure& cb) final
            {
                if (paused_) {
                    stored_packet_ = packet;
                    callback_ = cb;
                    return false;
                }
                if (IsRtcpPacket(&packet->data[0], packet->data.size())) {
                    ++number_of_rtcp_packets_;
                } else {
                    // Check that at least one RTCP packet was sent before the first RTP
                    // packet.  This confirms that the receiver will have the necessary lip
                    // sync info before it has to calculate the playout time of the first
                    // frame.
                    if (number_of_rtp_packets_ == 0)
                        EXPECT_LE(1, number_of_rtcp_packets_);
                    ++number_of_rtp_packets_;
                }
                return true;
            }

            int64_t GetBytesSent() final { return 0; }

            void StartReceiving(
                const PacketReceiverCallbackWithStatus& packet_receiver) final { }

            void StopReceiving() final { }

            int number_of_rtp_packets() const { return number_of_rtp_packets_; }

            int number_of_rtcp_packets() const { return number_of_rtcp_packets_; }

            void SetPause(bool paused)
            {
                paused_ = paused;
                if (!paused && stored_packet_.get()) {
                    SendPacket(stored_packet_, callback_);
                    callback_.Run();
                }
            }

        private:
            int number_of_rtp_packets_;
            int number_of_rtcp_packets_;
            bool paused_;
            base::Closure callback_;
            PacketRef stored_packet_;

            DISALLOW_COPY_AND_ASSIGN(TestPacketSender);
        };

        void IgnorePlayoutDelayChanges(base::TimeDelta unused_playout_delay)
        {
        }

        class PeerVideoSender : public VideoSender {
        public:
            PeerVideoSender(
                scoped_refptr<CastEnvironment> cast_environment,
                const FrameSenderConfig& video_config,
                const StatusChangeCallback& status_change_cb,
                const CreateVideoEncodeAcceleratorCallback& create_vea_cb,
                const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb,
                CastTransport* const transport_sender)
                : VideoSender(cast_environment,
                    video_config,
                    status_change_cb,
                    create_vea_cb,
                    create_video_encode_mem_cb,
                    transport_sender,
                    base::Bind(&IgnorePlayoutDelayChanges))
            {
            }
            using VideoSender::OnReceivedCastFeedback;
            using VideoSender::OnReceivedPli;
        };

        class TransportClient : public CastTransport::Client {
        public:
            TransportClient() { }

            void OnStatusChanged(CastTransportStatus status) final
            {
                EXPECT_EQ(TRANSPORT_STREAM_INITIALIZED, status);
            };
            void OnLoggingEventsReceived(
                std::unique_ptr<std::vector<FrameEvent>> frame_events,
                std::unique_ptr<std::vector<PacketEvent>> packet_events) final {};
            void ProcessRtpPacket(std::unique_ptr<Packet> packet) final {};

            DISALLOW_COPY_AND_ASSIGN(TransportClient);
        };

    } // namespace

    class VideoSenderTest : public ::testing::Test {
    protected:
        VideoSenderTest()
            : testing_clock_(new base::SimpleTestTickClock())
            , task_runner_(new FakeSingleThreadTaskRunner(testing_clock_))
            , cast_environment_(new CastEnvironment(
                  std::unique_ptr<base::TickClock>(testing_clock_),
                  task_runner_,
                  task_runner_,
                  task_runner_))
            , operational_status_(STATUS_UNINITIALIZED)
            , vea_factory_(task_runner_)
        {
            testing_clock_->Advance(base::TimeTicks::Now() - base::TimeTicks());
            vea_factory_.SetAutoRespond(true);
            last_pixel_value_ = kPixelValue;
            transport_ = new TestPacketSender();
            transport_sender_.reset(new CastTransportImpl(
                testing_clock_, base::TimeDelta(), base::MakeUnique<TransportClient>(),
                base::WrapUnique(transport_), task_runner_));
        }

        ~VideoSenderTest() override { }

        void TearDown() final
        {
            video_sender_.reset();
            task_runner_->RunTasks();
        }

        // If |external| is true then external video encoder (VEA) is used.
        // |expect_init_success| is true if initialization is expected to succeed.
        void InitEncoder(bool external, bool expect_init_success)
        {
            FrameSenderConfig video_config = GetDefaultVideoSenderConfig();
            video_config.use_external_encoder = external;

            ASSERT_EQ(operational_status_, STATUS_UNINITIALIZED);

            if (external) {
                vea_factory_.SetInitializationWillSucceed(expect_init_success);
                video_sender_.reset(new PeerVideoSender(
                    cast_environment_, video_config,
                    base::Bind(&SaveOperationalStatus, &operational_status_),
                    base::Bind(
                        &FakeVideoEncodeAcceleratorFactory::CreateVideoEncodeAccelerator,
                        base::Unretained(&vea_factory_)),
                    base::Bind(&FakeVideoEncodeAcceleratorFactory::CreateSharedMemory,
                        base::Unretained(&vea_factory_)),
                    transport_sender_.get()));
            } else {
                video_sender_.reset(new PeerVideoSender(
                    cast_environment_, video_config,
                    base::Bind(&SaveOperationalStatus, &operational_status_),
                    CreateDefaultVideoEncodeAcceleratorCallback(),
                    CreateDefaultVideoEncodeMemoryCallback(), transport_sender_.get()));
            }
            task_runner_->RunTasks();
        }

        scoped_refptr<media::VideoFrame> GetNewVideoFrame()
        {
            if (first_frame_timestamp_.is_null())
                first_frame_timestamp_ = testing_clock_->NowTicks();
            gfx::Size size(kWidth, kHeight);
            scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
                PIXEL_FORMAT_I420, size, gfx::Rect(size), size,
                testing_clock_->NowTicks() - first_frame_timestamp_);
            PopulateVideoFrame(video_frame.get(), last_pixel_value_++);
            return video_frame;
        }

        scoped_refptr<media::VideoFrame> GetLargeNewVideoFrame()
        {
            if (first_frame_timestamp_.is_null())
                first_frame_timestamp_ = testing_clock_->NowTicks();
            gfx::Size size(kWidth, kHeight);
            scoped_refptr<media::VideoFrame> video_frame = media::VideoFrame::CreateFrame(
                PIXEL_FORMAT_I420, size, gfx::Rect(size), size,
                testing_clock_->NowTicks() - first_frame_timestamp_);
            PopulateVideoFrameWithNoise(video_frame.get());
            return video_frame;
        }

        void RunTasks(int during_ms)
        {
            task_runner_->Sleep(base::TimeDelta::FromMilliseconds(during_ms));
        }

        base::SimpleTestTickClock* const testing_clock_; // Owned by CastEnvironment.
        const scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
        const scoped_refptr<CastEnvironment> cast_environment_;
        OperationalStatus operational_status_;
        FakeVideoEncodeAcceleratorFactory vea_factory_;
        TestPacketSender* transport_; // Owned by CastTransport.
        std::unique_ptr<CastTransportImpl> transport_sender_;
        std::unique_ptr<PeerVideoSender> video_sender_;
        int last_pixel_value_;
        base::TimeTicks first_frame_timestamp_;

    private:
        DISALLOW_COPY_AND_ASSIGN(VideoSenderTest);
    };

    TEST_F(VideoSenderTest, BuiltInEncoder)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();

        const base::TimeTicks reference_time = testing_clock_->NowTicks();
        video_sender_->InsertRawVideoFrame(video_frame, reference_time);

        task_runner_->RunTasks();
        EXPECT_LE(1, transport_->number_of_rtp_packets());
        EXPECT_LE(1, transport_->number_of_rtcp_packets());
    }

    TEST_F(VideoSenderTest, ExternalEncoder)
    {
        InitEncoder(true, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        // The SizeAdaptableExternalVideoEncoder initally reports STATUS_INITIALIZED
        // so that frames will be sent to it.  Therefore, no encoder activity should
        // have occurred at this point.  Send a frame to spurn creation of the
        // underlying ExternalVideoEncoder instance.
        if (vea_factory_.vea_response_count() == 0) {
            video_sender_->InsertRawVideoFrame(GetNewVideoFrame(),
                testing_clock_->NowTicks());
            task_runner_->RunTasks();
        }
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);
        RunTasks(33);

        // VideoSender created an encoder for 1280x720 frames, in order to provide the
        // INITIALIZED status.
        EXPECT_EQ(1, vea_factory_.vea_response_count());
        EXPECT_EQ(3, vea_factory_.shm_response_count());

        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();

        for (int i = 0; i < 3; ++i) {
            const base::TimeTicks reference_time = testing_clock_->NowTicks();
            video_sender_->InsertRawVideoFrame(video_frame, reference_time);
            RunTasks(33);
            // VideoSender re-created the encoder for the 320x240 frames we're
            // providing.
            EXPECT_EQ(1, vea_factory_.vea_response_count());
            EXPECT_EQ(3, vea_factory_.shm_response_count());
        }

        video_sender_.reset(NULL);
        task_runner_->RunTasks();
        EXPECT_EQ(1, vea_factory_.vea_response_count());
        EXPECT_EQ(3, vea_factory_.shm_response_count());
    }

    TEST_F(VideoSenderTest, ExternalEncoderInitFails)
    {
        InitEncoder(true, false);

        // The SizeAdaptableExternalVideoEncoder initally reports STATUS_INITIALIZED
        // so that frames will be sent to it.  Send a frame to spurn creation of the
        // underlying ExternalVideoEncoder instance, which should result in failure.
        if (operational_status_ == STATUS_INITIALIZED || operational_status_ == STATUS_CODEC_REINIT_PENDING) {
            video_sender_->InsertRawVideoFrame(GetNewVideoFrame(),
                testing_clock_->NowTicks());
            task_runner_->RunTasks();
        }
        EXPECT_EQ(STATUS_CODEC_INIT_FAILED, operational_status_);

        video_sender_.reset(NULL);
        task_runner_->RunTasks();
    }

    TEST_F(VideoSenderTest, RtcpTimer)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();

        const base::TimeTicks reference_time = testing_clock_->NowTicks();
        video_sender_->InsertRawVideoFrame(video_frame, reference_time);

        // Make sure that we send at least one RTCP packet.
        base::TimeDelta max_rtcp_timeout = base::TimeDelta::FromMilliseconds(1 + kRtcpReportIntervalMs * 3 / 2);

        RunTasks(max_rtcp_timeout.InMilliseconds());
        EXPECT_LE(1, transport_->number_of_rtp_packets());
        EXPECT_LE(1, transport_->number_of_rtcp_packets());
        // Build Cast msg and expect RTCP packet.
        RtcpCastMessage cast_feedback(1);
        cast_feedback.remote_ssrc = 2;
        cast_feedback.ack_frame_id = FrameId::first();
        video_sender_->OnReceivedCastFeedback(cast_feedback);
        RunTasks(max_rtcp_timeout.InMilliseconds());
        EXPECT_LE(1, transport_->number_of_rtcp_packets());
    }

    TEST_F(VideoSenderTest, ResendTimer)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();

        const base::TimeTicks reference_time = testing_clock_->NowTicks();
        video_sender_->InsertRawVideoFrame(video_frame, reference_time);

        // ACK the key frame.
        RtcpCastMessage cast_feedback(1);
        cast_feedback.remote_ssrc = 2;
        cast_feedback.ack_frame_id = FrameId::first();
        video_sender_->OnReceivedCastFeedback(cast_feedback);

        video_frame = GetNewVideoFrame();
        video_sender_->InsertRawVideoFrame(video_frame, reference_time);

        base::TimeDelta max_resend_timeout = base::TimeDelta::FromMilliseconds(1 + kDefaultRtpMaxDelayMs);

        // Make sure that we do a re-send.
        RunTasks(max_resend_timeout.InMilliseconds());
        // Should have sent at least 3 packets.
        EXPECT_LE(3, transport_->number_of_rtp_packets() + transport_->number_of_rtcp_packets());
    }

    TEST_F(VideoSenderTest, LogAckReceivedEvent)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        SimpleEventSubscriber event_subscriber;
        cast_environment_->logger()->Subscribe(&event_subscriber);

        int num_frames = 10;
        for (int i = 0; i < num_frames; i++) {
            scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();

            const base::TimeTicks reference_time = testing_clock_->NowTicks();
            video_sender_->InsertRawVideoFrame(video_frame, reference_time);
            RunTasks(33);
        }

        task_runner_->RunTasks();

        RtcpCastMessage cast_feedback(1);
        cast_feedback.ack_frame_id = FrameId::first() + num_frames - 1;

        video_sender_->OnReceivedCastFeedback(cast_feedback);

        std::vector<FrameEvent> frame_events;
        event_subscriber.GetFrameEventsAndReset(&frame_events);

        ASSERT_TRUE(!frame_events.empty());
        EXPECT_EQ(FRAME_ACK_RECEIVED, frame_events.rbegin()->type);
        EXPECT_EQ(VIDEO_EVENT, frame_events.rbegin()->media_type);
        EXPECT_EQ(FrameId::first() + num_frames - 1, frame_events.rbegin()->frame_id);

        cast_environment_->logger()->Unsubscribe(&event_subscriber);
    }

    TEST_F(VideoSenderTest, StopSendingInTheAbsenceOfAck)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        // Send a stream of frames and don't ACK; by default we shouldn't have more
        // than 4 frames in flight.
        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
        video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
        RunTasks(33);

        // Send 3 more frames and record the number of packets sent.
        for (int i = 0; i < 3; ++i) {
            scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
            video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
            RunTasks(33);
        }
        const int number_of_packets_sent = transport_->number_of_rtp_packets();

        // Send 3 more frames - they should not be encoded, as we have not received
        // any acks.
        for (int i = 0; i < 3; ++i) {
            scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
            video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
            RunTasks(33);
        }

        // We expect a frame to be retransmitted because of duplicated ACKs.
        // Only one packet of the frame is re-transmitted.
        EXPECT_EQ(number_of_packets_sent + 1, transport_->number_of_rtp_packets());

        // Start acking and make sure we're back to steady-state.
        RtcpCastMessage cast_feedback(1);
        cast_feedback.remote_ssrc = 2;
        cast_feedback.ack_frame_id = FrameId::first();
        video_sender_->OnReceivedCastFeedback(cast_feedback);
        EXPECT_LE(4, transport_->number_of_rtp_packets() + transport_->number_of_rtcp_packets());

        // Empty the pipeline.
        RunTasks(100);
        // Should have sent at least 7 packets.
        EXPECT_LE(7, transport_->number_of_rtp_packets() + transport_->number_of_rtcp_packets());
    }

    TEST_F(VideoSenderTest, DuplicateAckRetransmit)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
        video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
        RunTasks(33);
        RtcpCastMessage cast_feedback(1);
        cast_feedback.remote_ssrc = 2;
        cast_feedback.ack_frame_id = FrameId::first();

        // Send 3 more frames but don't ACK.
        for (int i = 0; i < 3; ++i) {
            scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
            video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
            RunTasks(33);
        }
        const int number_of_packets_sent = transport_->number_of_rtp_packets();

        // Send duplicated ACKs and mix some invalid NACKs.
        for (int i = 0; i < 10; ++i) {
            RtcpCastMessage ack_feedback(1);
            ack_feedback.remote_ssrc = 2;
            ack_feedback.ack_frame_id = FrameId::first();
            RtcpCastMessage nack_feedback(1);
            nack_feedback.remote_ssrc = 2;
            nack_feedback.missing_frames_and_packets[FrameId::first() + 255] = PacketIdSet();
            video_sender_->OnReceivedCastFeedback(ack_feedback);
            video_sender_->OnReceivedCastFeedback(nack_feedback);
        }
        EXPECT_EQ(number_of_packets_sent, transport_->number_of_rtp_packets());

        // Re-transmit one packet because of duplicated ACKs.
        for (int i = 0; i < 3; ++i) {
            RtcpCastMessage ack_feedback(1);
            ack_feedback.remote_ssrc = 2;
            ack_feedback.ack_frame_id = FrameId::first();
            video_sender_->OnReceivedCastFeedback(ack_feedback);
        }
        EXPECT_EQ(number_of_packets_sent + 1, transport_->number_of_rtp_packets());
    }

    TEST_F(VideoSenderTest, DuplicateAckRetransmitDoesNotCancelRetransmits)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
        video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
        RunTasks(33);
        RtcpCastMessage cast_feedback(1);
        cast_feedback.remote_ssrc = 2;
        cast_feedback.ack_frame_id = FrameId::first();

        // Send 2 more frames but don't ACK.
        for (int i = 0; i < 2; ++i) {
            scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
            video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
            RunTasks(33);
        }
        // Pause the transport
        transport_->SetPause(true);

        // Insert one more video frame.
        video_frame = GetLargeNewVideoFrame();
        video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
        RunTasks(33);

        const int number_of_packets_sent = transport_->number_of_rtp_packets();

        // Send duplicated ACKs and mix some invalid NACKs.
        for (int i = 0; i < 10; ++i) {
            RtcpCastMessage ack_feedback(1);
            ack_feedback.remote_ssrc = 2;
            ack_feedback.ack_frame_id = FrameId::first();
            RtcpCastMessage nack_feedback(1);
            nack_feedback.remote_ssrc = 2;
            nack_feedback.missing_frames_and_packets[FrameId::first() + 255] = PacketIdSet();
            video_sender_->OnReceivedCastFeedback(ack_feedback);
            video_sender_->OnReceivedCastFeedback(nack_feedback);
        }
        EXPECT_EQ(number_of_packets_sent, transport_->number_of_rtp_packets());

        // Re-transmit one packet because of duplicated ACKs.
        for (int i = 0; i < 3; ++i) {
            RtcpCastMessage ack_feedback(1);
            ack_feedback.remote_ssrc = 2;
            ack_feedback.ack_frame_id = FrameId::first();
            video_sender_->OnReceivedCastFeedback(ack_feedback);
        }

        transport_->SetPause(false);
        RunTasks(100);
        EXPECT_LT(number_of_packets_sent + 1, transport_->number_of_rtp_packets());
    }

    TEST_F(VideoSenderTest, AcksCancelRetransmits)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        transport_->SetPause(true);
        scoped_refptr<media::VideoFrame> video_frame = GetLargeNewVideoFrame();
        video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
        RunTasks(33);

        // Frame should be in buffer, waiting. Now let's ack it.
        RtcpCastMessage cast_feedback(1);
        cast_feedback.remote_ssrc = 2;
        cast_feedback.ack_frame_id = FrameId::first();
        video_sender_->OnReceivedCastFeedback(cast_feedback);

        transport_->SetPause(false);
        RunTasks(33);
        EXPECT_EQ(0, transport_->number_of_rtp_packets());
    }

    TEST_F(VideoSenderTest, CheckVideoFrameFactoryIsNull)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        EXPECT_EQ(nullptr, video_sender_->CreateVideoFrameFactory().get());
    }

    TEST_F(VideoSenderTest, PopulatesResourceUtilizationInFrameMetadata)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        for (int i = 0; i < 3; ++i) {
            scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
            ASSERT_FALSE(video_frame->metadata()->HasKey(
                media::VideoFrameMetadata::RESOURCE_UTILIZATION));

            const base::TimeTicks reference_time = testing_clock_->NowTicks();
            video_sender_->InsertRawVideoFrame(video_frame, reference_time);

            // Run encode tasks.  VideoSender::OnEncodedVideoFrame() will be called once
            // encoding of the frame is complete, and this is when the
            // RESOURCE_UTILIZATION metadata is populated.
            RunTasks(33);

            // Check that the RESOURCE_UTILIZATION value is set and non-negative.  Don't
            // check for specific values because they are dependent on real-world CPU
            // encode time, which can vary across test runs.
            double utilization = -1.0;
            EXPECT_TRUE(video_frame->metadata()->GetDouble(
                media::VideoFrameMetadata::RESOURCE_UTILIZATION, &utilization));
            EXPECT_LE(0.0, utilization);
            if (i == 0)
                EXPECT_GE(1.0, utilization); // Key frames never exceed 1.0.
            DVLOG(1) << "Utilization computed by VideoSender is: " << utilization;
        }
    }

    TEST_F(VideoSenderTest, CancelSendingOnReceivingPli)
    {
        InitEncoder(false, true);
        ASSERT_EQ(STATUS_INITIALIZED, operational_status_);

        // Send a frame and ACK it.
        scoped_refptr<media::VideoFrame> video_frame = GetNewVideoFrame();
        video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
        RunTasks(33);

        RtcpCastMessage cast_feedback(1);
        cast_feedback.remote_ssrc = 2;
        cast_feedback.ack_frame_id = FrameId::first();
        video_sender_->OnReceivedCastFeedback(cast_feedback);

        transport_->SetPause(true);
        // Send three more frames.
        for (int i = 0; i < 3; i++) {
            video_frame = GetNewVideoFrame();
            video_sender_->InsertRawVideoFrame(video_frame, testing_clock_->NowTicks());
            RunTasks(33);
        }
        EXPECT_EQ(1, transport_->number_of_rtp_packets());

        // Frames should be in buffer, waiting.
        // Received PLI from receiver.
        video_sender_->OnReceivedPli();
        video_frame = GetNewVideoFrame();
        video_sender_->InsertRawVideoFrame(
            video_frame,
            testing_clock_->NowTicks() + base::TimeDelta::FromMilliseconds(1000));
        RunTasks(33);
        transport_->SetPause(false);
        RunTasks(33);
        EXPECT_EQ(2, transport_->number_of_rtp_packets());
    }

} // namespace cast
} // namespace media
